package com.momoda.monitor;

import com.momoda.monitor.bean.FileInfo;
import com.momoda.monitor.bean.FileMetaInfo;
import com.momoda.monitor.config.MonitorFtpFolder;
import com.momoda.monitor.service.FileListener;
import com.momoda.monitor.util.FileUtil;
import com.momoda.monitor.util.PropertiesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.filefilter.FileFilterUtils;
import org.apache.commons.io.filefilter.HiddenFileFilter;
import org.apache.commons.io.filefilter.IOFileFilter;
import org.apache.commons.io.monitor.FileAlterationMonitor;
import org.apache.commons.io.monitor.FileAlterationObserver;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;


/**
 * 这里配置系统启动时也随之启动的方法调用
 */
@Slf4j
@Component
public class MyApplicationRunner implements ApplicationRunner {

    @Resource
    public MonitorFtpFolder monitorFtpFolder;

    @Override
    public void run(ApplicationArguments args) throws Exception {
        log.info("MyApplicationRunner class will be execute when the project was started!");
        MyApplicationRunner.restart();
        fileMonitorStart();

    }


    /**
     * 文件监控
     */
    public String fileMonitorStart() throws Exception {

        // 监控目录
        String rootDir = PropertiesUtil.getProperty("ftp.rootpath", "D:\\SFTP");

        String property = PropertiesUtil.getProperty("ftp.interval", "3");
        long interval1 = Long.parseLong(property);
        // 轮询间隔 5 秒
        long interval = TimeUnit.SECONDS.toMillis(interval1);
        // 创建过滤器  目录过滤器
        IOFileFilter dir = FileFilterUtils.and(FileFilterUtils.directoryFileFilter(), HiddenFileFilter.VISIBLE);
        // 不要info.json文件
        IOFileFilter file1 = FileFilterUtils.notFileFilter(FileFilterUtils.nameFileFilter("info.json"));
        //不要~$开头文件夹 修改word文件时，会产生临时文件以此开头
        IOFileFilter file2 = FileFilterUtils.notFileFilter(FileFilterUtils.prefixFileFilter("~$"));
        IOFileFilter file = FileFilterUtils.and(file1, file2);

//        目录和文件结合
        IOFileFilter filter = FileFilterUtils.or(dir, file);
        File file3 = new File(rootDir);
        if (!file3.exists()) {
            file3.mkdir();
        }
        // 使用过滤器
        FileAlterationObserver observer = new FileAlterationObserver(new File(rootDir), filter);

        observer.addListener(new FileListener());
        //创建文件变化监听器
        FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
//        monitor = new FileAlterationMonitor(interval, observer);
        // 开始监控
        monitor.start();
        log.info("文件监控启动成功，正在监控中，目前 " + property + " 秒监控文件变化");
        return "start success";
    }


    public static void restart() {
        String rootDir = PropertiesUtil.getProperty("ftp.rootpath", "D:\\SFTP");
        File file3 = new File(rootDir);
        if (!file3.exists()) {
            file3.mkdir();
        }
//        1.收集所有文件
        Collection<File> files = FileUtils.listFiles(new File(rootDir), null, true);
//        2.收集info.json所在文件节
//          3. 遍历info.josn所在文件夹的当前目录和子目录，排除info.json添加到


//        1. 从配置文件中读取监控目录,一次获取配置文件下一级用户目录，
        FileUtil fileUtil = FileUtil.getFileUtil();
        Set<String> pathSets = fileUtil.readProperties();
        for (String path : pathSets) { //parh=D:/SFTP/clients/ThingJS/

//          1.  path = 当前目录 /ThingJS

//            2. path 进入下一级用户目录
            File file = new File(path);
            if (file.exists()) {
                log.info("进入监控的每一项根目录： {} ", file.getAbsolutePath().replaceAll("\\\\", "/"));
                File[] totalUserDir = file.listFiles();
                for (File userDir : totalUserDir) {
                    if (userDir.isDirectory()) {//遍历用户目录下修改info.json

                        File infoFile = null;
                        Collection<File> childFiles = FileUtils.listFiles(userDir, null, true);
                        log.info("userid下所有文件 : {}", childFiles.toString());
                        //3. 判断 有info.json移除，没有创建
                        Iterator<File> iterator = childFiles.iterator();
                        boolean flag = false;//没有info.josn则创建
                        while (iterator.hasNext()) {
                            File next = iterator.next();
                            if (next.getAbsolutePath().replaceAll("\\\\", "/").endsWith("info.json")) {
                                iterator.remove();
                                infoFile = next;
                                flag = true;
                            }
                            if (flag) {
                                break;
                            }

                        }
                        if (!flag) {
                            infoFile = FileUtil.fileCreateIfNotExist(userDir.getAbsolutePath().replaceAll("\\\\", "/") + File.separator + "info.json");
                        }

                        // 4. 读取配置文件,转成json
                        FileMetaInfo metaInfo = FileUtil.readInfoFileToBean(infoFile);

                        if (!CollectionUtils.isEmpty(childFiles)) {
                            //5.
                            for (File childFile : childFiles) {
                                String absolutePath = childFile.getAbsolutePath().replaceAll("\\\\", "/");
                                String lastModified = String.valueOf(childFile.lastModified());
                                //todo md5 和exits
                                log.info("文件路径：{}", absolutePath);
                                log.info("最后修改时间：{} ", lastModified);
                                boolean isChange = false;
                                boolean isCreate = false;
                                //1. 首先遍历改变列表，设置最新时间
                                List<FileInfo> changeFile = metaInfo.getChangeFile();
                                if (!CollectionUtils.isEmpty(changeFile)) {
                                    for (int i = 0; i < changeFile.size(); i++) {
                                        if (absolutePath.equals(changeFile.get(i).getPath())) {
                                            //设置最新时间
                                            changeFile.get(i).setTime(lastModified);
                                            changeFile.get(i).setExist("0");

                                            String md5 = generateMd5FromFile(absolutePath);
                                            changeFile.get(i).setHash(md5);
                                            isChange = true;
                                            isCreate = false;
                                            break;
                                        }
                                    }
                                }

                                //2. 遍历创建列表 如果改变列表没有，怎遍历创建列表，
                                //todo 如果创建列表存在，但是时间小于目前时间，则表示被修改过，需要把该文件从创建列表移除到改变列表，目前暂时不移动，只更新时间
                                if (!isChange) {
                                    List<FileInfo> createFile = metaInfo.getCreateFile();
                                    if (createFile != null) {

                                        for (int i = 0; i < createFile.size(); i++) {
                                            if (absolutePath.equals(createFile.get(i).getPath())) {
                                                if (Long.parseLong(createFile.get(i).getTime()) < Long.parseLong(lastModified)) {
                                                    changeFile.add(new FileInfo(absolutePath, lastModified, "0"));
                                                    metaInfo.setChangeFile(changeFile);
                                                    isChange = true;
                                                    isCreate = false;
                                                } else {
                                                    createFile.get(i).setTime(lastModified);
                                                    createFile.get(i).setExist("0");

                                                    String md5 = generateMd5FromFile(absolutePath);

                                                    createFile.get(i).setHash(md5);
                                                    isChange = false;
                                                    isCreate = true;
                                                }
                                                break;

                                            }
                                        }
                                    }
                                }
                                //3. 如果改变和创建都没有，则添加到创建里面去
                                if (isChange == false && isCreate == false) {
                                    List<FileInfo> createFile = metaInfo.getCreateFile();
                                    if (createFile == null) {
                                        createFile = new ArrayList<>();
                                        createFile.add(new FileInfo(absolutePath, lastModified, "0"));
                                        metaInfo.setCreateFile(createFile);
                                    } else {
                                        metaInfo.getCreateFile().add(new FileInfo(absolutePath, lastModified, "0"));
                                    }
                                }

                                //4. 如果删除列表中有这个文件，则删除记录
                                //todo 如果文件被删

                            }

                        } else {
                            metaInfo.setCreateFile(new ArrayList<>());
                            metaInfo.setChangeFile(new ArrayList<>());
                        }


                        //删除，记录，但不添加remove中
                        removeFile(metaInfo.getCreateFile());
                        removeFile(metaInfo.getChangeFile());
                        //重置exist编制
                        reset(metaInfo.getCreateFile());
                        reset(metaInfo.getChangeFile());

                        FileUtil.writeBeanToInfoFile(infoFile, metaInfo);

                    }// 进入分项目录的userid目录
                }
            } else {
                log.info(file.getAbsolutePath().replaceAll("\\\\", "/") + "不存在");

            }
        }
    }

    /**
     * 根据路径读取文件生成MD5
     *
     * @param absolutePath
     * @return
     */
    public static String generateMd5FromFile(String absolutePath) {
        String md5 = null;
        FileInputStream fileIn = null;
        try {
            fileIn = new FileInputStream(new File(absolutePath));
            md5 = DigestUtils.md5Hex(fileIn);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                fileIn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return md5;
    }


    public static void main(String[] args) throws IOException {
        String rootDir = "D:\\SFTP";

//        1.收集所有文件
        Collection<File> files = FileUtils.listFiles(new File(rootDir), null, true);
//        2.收集info.json所在文件节
//          3. 遍历info.josn所在文件夹的当前目录和子目录，排除info.json添加到


//        1. 从配置文件中读取监控目录,一次获取配置文件下一级用户目录，
        FileUtil fileUtil = FileUtil.getFileUtil();
        Set<String> pathSets = fileUtil.readProperties();
        for (String path : pathSets) { //parh=D:/SFTP/clients/ThingJS/

//          1.  path = 当前目录 /ThingJS

//            2. path 进入下一级用户目录
            File file = new File(path);
            if (file.exists()) {
                log.info("进入监控的每一项根目录： {} ", file.getAbsolutePath());
                File[] totalUserDir = file.listFiles();
                for (File userDir : totalUserDir) {
                    if (userDir.isDirectory()) {//遍历用户目录下修改info.json

                        File infoFile = null;
                        Collection<File> childFiles = FileUtils.listFiles(userDir, null, true);
                        log.info("userid下所有文件 : {}", childFiles.toString());
                        //3. 判断 有info.json移除，没有创建
                        Iterator<File> iterator = childFiles.iterator();
                        boolean flag = false;//没有info.josn则创建x
                        while (iterator.hasNext()) {
                            File next = iterator.next();
                            if (next.getAbsolutePath().endsWith("info.json")) {
                                iterator.remove();
                                infoFile = next;
                                flag = true;
                            }
                            if (flag) {
                                break;
                            }

                        }
                        if (!flag) {
                            infoFile = FileUtil.fileCreateIfNotExist(userDir.getAbsolutePath() + File.separator + "info.json");
                        }

                        // 4. 读取配置文件,转成json
                        FileMetaInfo metaInfo = FileUtil.readInfoFileToBean(infoFile);

                        if (!CollectionUtils.isEmpty(childFiles)) {
                            //5.
                            for (File childFile : childFiles) {
                                String absolutePath = childFile.getAbsolutePath();
                                String lastModified = String.valueOf(childFile.lastModified());
                                //todo md5 和exits
                                log.info("文件路径：{}", absolutePath);
                                log.info("最后修改时间：{} ", lastModified);
                                boolean isChange = false;
                                boolean isCreate = false;
                                //1. 首先遍历改变列表，设置最新时间
                                List<FileInfo> changeFile = metaInfo.getChangeFile();
                                if (!CollectionUtils.isEmpty(changeFile)) {
                                    for (int i = 0; i < changeFile.size(); i++) {
                                        if (absolutePath.equals(changeFile.get(i).getPath())) {
                                            //设置最新时间
                                            changeFile.get(i).setTime(lastModified);
                                            changeFile.get(i).setExist("0");
                                            isChange = true;
                                            isCreate = false;
                                            break;
                                        }
                                    }
                                }

                                //2. 遍历创建列表 如果改变列表没有，怎遍历创建列表，
                                //todo 如果创建列表存在，但是时间小于目前时间，则表示被修改过，需要把该文件从创建列表移除到改变列表，目前暂时不移动，只更新时间
                                if (!isChange) {
                                    List<FileInfo> createFile = metaInfo.getCreateFile();
                                    if (createFile != null) {

                                        for (int i = 0; i < createFile.size(); i++) {
                                            if (absolutePath.equals(createFile.get(i).getPath())) {
                                                if (Long.parseLong(createFile.get(i).getTime()) < Long.parseLong(lastModified)) {
                                                    changeFile.add(new FileInfo(absolutePath, lastModified, "0"));
                                                    metaInfo.setChangeFile(changeFile);
                                                    isChange = true;
                                                    isCreate = false;
                                                } else {
                                                    createFile.get(i).setTime(lastModified);
                                                    createFile.get(i).setExist("0");
                                                    isChange = false;
                                                    isCreate = true;
                                                }
                                                break;

                                            }
                                        }
                                    }
                                }
                                //3. 如果改变和创建都没有，则添加到创建里面去
                                if (isChange == false && isCreate == false) {
                                    List<FileInfo> createFile = metaInfo.getCreateFile();
                                    if (createFile == null) {
                                        createFile = new ArrayList<>();
                                        createFile.add(new FileInfo(absolutePath, lastModified, "0"));
                                        metaInfo.setCreateFile(createFile);
                                    } else {
                                        metaInfo.getCreateFile().add(new FileInfo(absolutePath, lastModified, "0"));
                                    }
                                }

                                //4. 如果删除列表中有这个文件，则删除记录
                                //todo 如果文件被删

                            }

                        } else {
                            metaInfo.setCreateFile(new ArrayList<>());
                            metaInfo.setChangeFile(new ArrayList<>());
                        }


                        //删除，记录，但不添加remove中
                        removeFile(metaInfo.getCreateFile());
                        removeFile(metaInfo.getChangeFile());
                        //重置exist编制
                        reset(metaInfo.getCreateFile());
                        reset(metaInfo.getChangeFile());

                        FileUtil.writeBeanToInfoFile(infoFile, metaInfo);

                    }// 进入分项目录的userid目录
                }
            } else {
                log.info(file.getAbsolutePath().replaceAll("\\\\", "/") + "不存在");

            }
        }
    }

    public static void clear(List<FileInfo> fileInfos) {
        if (fileInfos == null) {
            return;
        } else {

        }
    }

    /**
     * 重置 exist 标志
     */
    public static void reset(List<FileInfo> fileInfos) {
        if (fileInfos == null) {
            return;
        }
        for (int i = 0; i < fileInfos.size(); i++) {
            fileInfos.get(i).setExist("1");
        }
    }

    public boolean isExist(List<FileInfo> createFile, String path) {
        return false;
    }


    /**
     * 删除标志位1 的文件
     *
     * @param fileInfos
     */
    public static void removeFile(List<FileInfo> fileInfos) {
        if (fileInfos == null) {
            return;
        }
        Iterator<FileInfo> iterator = fileInfos.iterator();
        while (iterator.hasNext()) {
            FileInfo next = iterator.next();
            if (next.getExist().equals("1")) {
                iterator.remove();
            }
//            else {
//                next.setExist("1");
//            }
        }
    }


}

